#!/usr/bin/env python
# -*- coding: UTF-8 -*-
from flask.ext.sqlalchemy import SQLAlchemy
from sqlalchemy import MetaData

metadata = MetaData()
db = SQLAlchemy()


class BaseModel(db.Model):
    """
    抽象类，其他Model基于此
    """
    __abstract__ = True

    id = db.Column(db.Integer, primary_key=True)

    @classmethod
    def create_base(cls, **kwargs):
        """
        use to create a new data
        :param session:
        :param kwargs:
        :return:
        """
        _new = cls.create_base_without_commit(**kwargs)
        db.session.commit()
        return _new

    @classmethod
    def create_base_without_commit(cls, **kwargs):
        """
        create but not commit, use for transaction
        :param session:
        :param kwargs:
        :return:
        """
        _new = cls(**kwargs)
        db.session.add(_new)
        db.session.flush()
        return _new

    @classmethod
    def exist(cls, id):
        obj = cls.get_by_id(id)
        return True if obj else False

    @classmethod
    def get_by_id(cls, id):
        return db.session.query(cls).get(id)

    @classmethod
    def get_one(cls):
        return db.session.query(cls).first()

    @classmethod
    def get_by_ids(cls, ids):
        return db.session.query(cls).filter(
            cls.id.in_(ids),
        )

    @classmethod
    def get_by_args(cls, **kwargs):
        return db.session.query(cls).filter_by(**kwargs).first()

    @classmethod
    def get_all_base(cls):
        return db.session.query(cls).order_by(cls.id)

    @classmethod
    def get_valid(cls):
        return db.session.query(cls).filter(cls.status==1)

    @classmethod
    def get_all_base_by_index(cls):
        return db.session.query(cls).order_by(cls.index)

    @classmethod
    def get_all_show(cls):
        return db.session.query(cls).filter(cls.show == True)

    @classmethod
    def get_column_set(cls):
        """
       
        取ORM模型对应表字段
        :return:set
        """
        return set([col.name for col in cls.__table__.c])

    @classmethod
    def get_pkey_set(cls):
        """
       
        取ORM模型对应表主键
        :return:set
        """
        return set([pk.name for pk in cls.__table__.primary_key])

    @classmethod
    def get_fkey_set(cls):
        """
        取ORM模型对应表外键
        :return:set
        """
        return set([fkey.parent.name for fkey in cls.__table__.foreign_keys])

    @classmethod
    def get_id_name_choices(cls):
        """
        取以(id,name)的choices
        """
        return [(column.id, column.name) for column in db.session.query(cls)]

    def update_base_without_commit(self, **kwargs):
        for k, v in kwargs.items():
            setattr(self, k, v)
        db.session.flush()

    def update_base(self, **kwargs):
        """
        use to update data itself
        :param session:
        :param kwargs:
        :return:
        """
        self.update_base_without_commit(**kwargs)
        db.session.commit()

    def update_by_form(self, form, commit=True):
        """
       
        根据form表单提供的数据，更新ORM模型
        不支持更新外键，是因为外键涉及到是否存在的问题
        """
        # 只更新 非主键，非外键 的字段
        column_set = self.get_column_set()
        pk_column_set = self.get_pkey_set()
        fkey_column_set = self.get_fkey_set()
        for field in form.data:
            if field in (column_set - pk_column_set - fkey_column_set):
                setattr(self, field, form.data[field])
        if commit:
            db.session.commit()

    @classmethod
    def select_for_update(cls, **kwargs):
        """
        
        select object for update
        """
        return db.session.query(cls).with_for_update(
            nowait=True,
            of=cls
        ).filter_by(
            **kwargs
        ).first()

    @classmethod
    def create_as_check_col(self, **kwargs):
        for k,v in kwargs.items():
            if k not in self.get_column_set():
                del kwargs[k]
        self.create_base_without_commit(**kwargs)

    @classmethod
    def delete_batch(cls, to_delete_list):
        """
        
        批量删除
        :param session:
        :param to_delete_list:
        :return:
        """
        db.session.query(cls).filter(
            cls.id.in_(tuple(to_delete_list))
        ).delete(synchronize_session='fetch')
        db.session.commit()

    @classmethod
    def update_batch(cls, update_ids, kwargs):
        """
        批量更新
        """
        db.session.query(cls).filter(
            cls.id.in_(update_ids),
        ).update(kwargs, synchronize_session='fetch')
        db.session.commit()

    @classmethod
    def update_batch_without_commit(cls, update_ids, kwargs):
        """
        批量更新,不提交
        """
        db.session.query(cls).filter(
            cls.id.in_(update_ids),
        ).update(kwargs, synchronize_session='fetch')
        db.session.flush()